package gang.org.springframework.framework.util;

import gang.org.springframework.framework.metadata.GangConcurrentReferenceHashMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ChenGang
 */
public abstract class GangReflectionUtils {

    public static final MethodFilter USER_DECLARED_METHODS = (method -> !method.isBridge() && !method.isSynthetic());

    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

    private static final Map<Class<?>, Method[]> declaredMethodsCache = new ConcurrentHashMap<>();

    private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];

    public static Object invokeMethod(Method method, Object target){
        return invokeMethod(method,target,EMPTY_OBJECT_ARRAY);
    }

    public static Object invokeMethod(Method method,Object target, Object... args){
        try {
            return method.invoke(target,args);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }


    public static Method[] getUniqueDeclaredMethods(Class<?> leafClass, MethodFilter mf){
        final List<Method> methods = new ArrayList<>();
        doWithMethods(leafClass, method -> {
            //TODO 函数式接口真正实现
            methods.add(method);
        }, mf);
        return methods.toArray(EMPTY_METHOD_ARRAY);
    }

    /**
     * 业务意义，解析类本身，父类
     * WebMvcAutoConfiguration(主类)
     *  EnableWebMvcConfiguration(内部类)
     *  extend DelegatingWebMvcConfiguration(父类)
     *  extend WebMvcConfigurationSupport(父类)
     * */
    public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf){
        /*
         * 业务场景一
         * 获取类本身的方法
         * */
        Method[] methods = getDeclaredMethods(clazz, false);
        for (Method method : methods) {
            //lambda 表达式过滤
            if (mf != null && !mf.matches(method)) {
                continue;
            }
            try {
                mc.doWith(method);
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        /**
         * 业务场景二
         * 获取父类的方法
         * */
        if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
            Class<?> superclass = clazz.getSuperclass();
            doWithMethods(superclass, mc, mf);
        }

        /**
         * 业务场景三
         * 获取类本身是接口
         * */
        //TODO
    }

    public static  Method[] getDeclaredMethods(Class<?> clazz, boolean defensive){
        Method[] methods = declaredMethodsCache.get(clazz);
        if (null == methods) {
            Method[] declaredMethods = clazz.getDeclaredMethods();
            //TODO
            return declaredMethods;
        }
        return methods;
    }


    @FunctionalInterface
    public interface MethodCallback {
        /**
         * Perform an operation using the given method.
         * @param method the method to operate on
         */
        void doWith(Method method);
    }

    @FunctionalInterface
    public interface MethodFilter{
        /**
         * Determine whether the given method matches.
         * @param method the method to check
         * @return boolean value if match
         */
        boolean matches(Method method);
    }

    public static void doWithLocalMethod(Class<?> clazz, MethodCallback mc){
        Method[] methods = getDeclaredMethods(clazz, false);
        for (Method method : methods) {
            mc.doWith(method);
        }
    }
}
